#include "TreeFile.h"
#include "SerialInator.h"


TreeFile::TreeFile(std::string nomArch, size_t tamBloques, bool crear){

	// Setea el tamaño de los bloques
	this->_tamBloques = tamBloques;

	// Setea el modo de apertura del archivo
	std::ios::openmode modo = std::ios::binary | std::ios::in | std::ios::out;
	if (crear) modo |= std::ios::trunc;

	// Abre el archivo (o lo crea si es necesario)
	this->_archArbol.open(nomArch.c_str(), modo);

	char* dataBuff = new char[this->_tamBloques];

	if (crear){
		// Carga los datos por defecto de la cabecera y la guarda a disco
		this->_metaData.pack(dataBuff);
		storeBloque(0, dataBuff);

	} else {
		loadBloque(0, dataBuff);
		this->_metaData.unpack(dataBuff);
	}

	delete[] dataBuff;
}

TreeFile::~TreeFile(){
	// Guarda los cambios de la cabecera al archivo
	char* temp = new char[this->_tamBloques];

	// Serializa los datos y los guarda en la cabecera
	this->_metaData.pack(temp);
	storeBloque(0, temp);

	delete[] temp;

	// Cierra el archivo
	this->_archArbol.close();
}


void TreeFile::loadBloque(unsigned long int refPosBloque, char* outData){
	// Calcula el offset de lectura del archivo
	int pos = this->_tamBloques * refPosBloque;

	// Realiza la lectura de los datos al buffer
	this->_archArbol.seekg(pos, std::ios::beg);
	this->_archArbol.read(outData, this->_tamBloques);
}

void TreeFile::storeBloque(unsigned long int refPosBloque, char* inData){
	// Calcula el offset de escritura del archivo
	int pos = this->_tamBloques * refPosBloque;

	// Escribe los datos al buffer pasado
	this->_archArbol.seekp(pos, std::ios::beg);
	this->_archArbol.write(inData, this->_tamBloques);

	// Si el bloque que se guardo es mayor a la cantidad de bloques que existen
	// es porque se inserto un bloque nuevo y hay que actualizar la cantidad
	if (refPosBloque == this->_metaData.getCantBloques())
		this->_metaData.setCantBloques(this->_metaData.getCantBloques()+1);
}

void TreeFile::deleteBloque(unsigned long int refPosBloque){
	// Para guardar temporalmente el bloque a borrar
	char* dataTemp = new char[this->_tamBloques];

	// Lee el bloque a ser borrado
	loadBloque(refPosBloque, dataTemp);

	// Escribe en el bloque la posicion del anterior bloque vacio
	SerialInator s;
	s.serializar(dataTemp, this->_metaData.getRefBloqueVacio());

	// Guarda en disco el bloque "borrado"
	storeBloque(refPosBloque, dataTemp);

	// Actualiza la referencia de bloque vacio para que apunte 
	// al recien borrado
	this->_metaData.setRefBloqueVacio(refPosBloque);

	delete[] dataTemp;
}

unsigned long int TreeFile::getNewBloque(){
	// Primero chequea si existe una vacio
	if (this->_metaData.getRefBloqueVacio() != -1){

		// Existe un bloque vacio/borrado, se la recupera
		char* dataTemp = new char[this->_tamBloques];
		loadBloque(this->_metaData.getRefBloqueVacio(), dataTemp);

		// Se obtiene el valor almacenado en el bloque 
		SerialInator s;
		unsigned long int refTemp =0;
		s.desSerializar(dataTemp, refTemp);

		// Se actualizan los valores
		unsigned long int refbloque = this->_metaData.getRefBloqueVacio();
		this->_metaData.setRefBloqueVacio(refTemp);
		delete[] dataTemp;

		// Se devuelve la referencia al bloque rescatado
		return refbloque;
	}

	// "Crea" un nuevo bloque, en realidad da una referencia a un bloque 
	// que todavia no existe, este se crea al hacer store del mismo
	return this->_metaData.getCantBloques();
}

unsigned long int TreeFile::getCantBloques(){
	return this->_metaData.getCantBloques();
}

size_t TreeFile::getSizeBloque(){
	return this->_tamBloques;
}

unsigned long int TreeFile::getRefBloqueRaiz(){
	return this->_metaData.getRefBloqueRaiz();
}


unsigned long int TreeFile::getRefHoja(){
	return this->_metaData.getRefBloqueHoja();
}

